"""用于调用openocd烧录文件到指定的fpga板"""
from abc import ABCMeta
from abc import abstractmethod
from typing import Optional
import logging
import subprocess

logger = logging.getLogger('logger')


# 这是一个接口类，烧录函数至少需要两个参数，分别是usb序列号、bit流文件路径
class OpenOcd(metaclass=ABCMeta):
    """抽象类，拓展其他烧录方法时继承该类"""

    @abstractmethod
    # others是未来可能出现的其他参数，返回烧录是否成功
    def program(self, cfg_path: str, serial_num: str, bit_path: str, *others: str) -> bool:
        pass


class Xc7OpenOcd(OpenOcd):
    __serial = 'set _SERIAL '
    __bit_file_path = 'set _BIT_FILE_PATH '

    def program(self, cfg_path: str, serial_num: str, bit_path: str, *others: str) -> bool:
        cmd1 = self.__serial + serial_num
        cmd2 = self.__bit_file_path + bit_path
        # 注意：这是一个阻塞方法
        ret = subprocess.run(f'openocd -c "{cmd1}" -c "{cmd2}" -f {cfg_path}', capture_output=True, encoding='ascii')
        # logger.debug(ret.stdout)
        if ret.returncode != 0:
            logger.error("program failed")
            logger.error(ret.stderr)
            logger.error(ret.stdout)
            return False
        if 'Error' in ret.stderr:
            logger.error("program failed")
            logger.error(ret.stderr)
            return False
        if 'Error' in ret.stdout:
            logger.error("program failed")
            logger.error(ret.stdout)
            return False
        return True


def get_program_func(fpga_tag: str) -> Optional[Xc7OpenOcd]:
    if fpga_tag == 'M0':
        return Xc7OpenOcd()
    elif fpga_tag == 'E0':
        return Xc7OpenOcd()
    else:
        return None

# debug
# if __name__ == "__main__":
#     o = Xc7OpenOcd()
#     o.program(r'openocd/xc7.cfg', 'M01316546565', r'bit_file/minisys.bit')
